#include <wx/wxprec.h>

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif

#include "wxosqlite.h"

//Error messages
const wxChar* wxERRMSG_NODB          = wxTRANSLATE("No Database opened");
const wxChar* wxERRMSG_NOSTMT        = wxTRANSLATE("Statement not accessible");
const wxChar* wxERRMSG_NOMEM         = wxTRANSLATE("Out of memory");
const wxChar* wxERRMSG_DECODE        = wxTRANSLATE("Cannot decode binary");
const wxChar* wxERRMSG_INVALID_INDEX = wxTRANSLATE("Invalid field index");
const wxChar* wxERRMSG_INVALID_NAME  = wxTRANSLATE("Invalid field name");
const wxChar* wxERRMSG_INVALID_ROW   = wxTRANSLATE("Invalid row index");
const wxChar* wxERRMSG_INVALID_QUERY = wxTRANSLATE("Invalid scalar query");
const wxChar* wxERRMSG_INVALID_BLOB  = wxTRANSLATE("Invalid BLOB handle");

const wxChar* wxERRMSG_NORESULT      = wxTRANSLATE("Null Results pointer");
const wxChar* wxERRMSG_BIND_STR      = wxTRANSLATE("Error binding string param");
const wxChar* wxERRMSG_BIND_INT      = wxTRANSLATE("Error binding int param");
const wxChar* wxERRMSG_BIND_INT64    = wxTRANSLATE("Error binding int64 param");
const wxChar* wxERRMSG_BIND_DBL      = wxTRANSLATE("Error binding double param");
const wxChar* wxERRMSG_BIND_BLOB     = wxTRANSLATE("Error binding blob param");
const wxChar* wxERRMSG_BIND_DATETIME = wxTRANSLATE("Error binding date/time param");
const wxChar* wxERRMSG_BIND_NULL     = wxTRANSLATE("Error binding NULL param");
const wxChar* wxERRMSG_BIND_ZEROBLOB = wxTRANSLATE("Error binding zero blob param");
const wxChar* wxERRMSG_BIND_CLEAR    = wxTRANSLATE("Error clearing bindings");

const wxChar* wxERRMSG_NOMETADATA    = wxTRANSLATE("Meta data support not available");
const wxChar* wxERRMSG_NOCODEC       = wxTRANSLATE("Encryption support not available");
const wxChar* wxERRMSG_NOLOADEXT     = wxTRANSLATE("Loadable extension support not available");
const wxChar* wxERRMSG_NOINCBLOB     = wxTRANSLATE("Incremental BLOB support not available");

const wxChar* wxERRMSG_SHARED_CACHE  = wxTRANSLATE("Setting SQLite shared cache mode failed");

inline wxString UTF8toWxString(const char* localValue)
{
#if wxUSE_UNICODE
	return wxString(localValue, wxConvUTF8);
	#else
	return wxString(wxConvUTF8.cMB2WC(localValue), *wxConvCurrent);
#endif
}

//-----------------------------------------------------------
//  wxSQLException class
//-----------------------------------------------------------
wxSQLException::wxSQLException(int err, const wxString &msg)
: errcode(err)
{
	errmsg = ErrorCodeAsString(err) + _T("[") +
		wxString::Format(_T("%d"),err) + _T("]:") +
		wxGetTranslation(msg);
}
wxSQLException::wxSQLException(const wxSQLException &e)
:errcode(e.errcode), errmsg(e.errmsg)
{

}
wxSQLException::~wxSQLException(void)
{

}
const wxString wxSQLException::ErrorCodeAsString(int code)
{
  switch (code)
  {
    case SQLITE_OK          : return _T("SQLITE_OK");
    case SQLITE_ERROR       : return _T("SQLITE_ERROR");
    case SQLITE_INTERNAL    : return _T("SQLITE_INTERNAL");
    case SQLITE_PERM        : return _T("SQLITE_PERM");
    case SQLITE_ABORT       : return _T("SQLITE_ABORT");
    case SQLITE_BUSY        : return _T("SQLITE_BUSY");
    case SQLITE_LOCKED      : return _T("SQLITE_LOCKED");
    case SQLITE_NOMEM       : return _T("SQLITE_NOMEM");
    case SQLITE_READONLY    : return _T("SQLITE_READONLY");
    case SQLITE_INTERRUPT   : return _T("SQLITE_INTERRUPT");
    case SQLITE_IOERR       : return _T("SQLITE_IOERR");
    case SQLITE_CORRUPT     : return _T("SQLITE_CORRUPT");
    case SQLITE_NOTFOUND    : return _T("SQLITE_NOTFOUND");
    case SQLITE_FULL        : return _T("SQLITE_FULL");
    case SQLITE_CANTOPEN    : return _T("SQLITE_CANTOPEN");
    case SQLITE_PROTOCOL    : return _T("SQLITE_PROTOCOL");
    case SQLITE_EMPTY       : return _T("SQLITE_EMPTY");
    case SQLITE_SCHEMA      : return _T("SQLITE_SCHEMA");
    case SQLITE_TOOBIG      : return _T("SQLITE_TOOBIG");
    case SQLITE_CONSTRAINT  : return _T("SQLITE_CONSTRAINT");
    case SQLITE_MISMATCH    : return _T("SQLITE_MISMATCH");
    case SQLITE_MISUSE      : return _T("SQLITE_MISUSE");
    case SQLITE_NOLFS       : return _T("SQLITE_NOLFS");
    case SQLITE_AUTH        : return _T("SQLITE_AUTH");
    case SQLITE_FORMAT      : return _T("SQLITE_FORMAT");
    case SQLITE_RANGE       : return _T("SQLITE_RANGE");
    case SQLITE_NOTADB      : return _T("SQLITE_NOTADB");
    case SQLITE_ROW         : return _T("SQLITE_ROW");
    case SQLITE_DONE        : return _T("SQLITE_DONE");
    // Extended error codes
    case SQLITE_IOERR_READ       : return _T("SQLITE_IOERR_READ");
    case SQLITE_IOERR_SHORT_READ : return _T("SQLITE_IOERR_SHORT_READ");
    case SQLITE_IOERR_WRITE      : return _T("SQLITE_IOERR_WRITE");
    case SQLITE_IOERR_FSYNC      : return _T("SQLITE_IOERR_FSYNC");
    case SQLITE_IOERR_DIR_FSYNC  : return _T("SQLITE_IOERR_DIR_FSYNC");
    case SQLITE_IOERR_TRUNCATE   : return _T("SQLITE_IOERR_TRUNCATE");
    case SQLITE_IOERR_FSTAT      : return _T("SQLITE_IOERR_FSTAT");
    case SQLITE_IOERR_UNLOCK     : return _T("SQLITE_IOERR_UNLOCK");
    case SQLITE_IOERR_RDLOCK     : return _T("SQLITE_IOERR_RDLOCK");
    case SQLITE_IOERR_DELETE     : return _T("SQLITE_IOERR_DELETE");
    case SQLITE_IOERR_BLOCKED    : return _T("SQLITE_IOERR_BLOCKED");

    case WXSQL_ERROR     : return _T("WXSQLITE_ERROR");
    default                 : return _T("UNKNOWN_ERROR");
  }
}

//-----------------------------------------------------------
//  wxSQLResultSet class
//-----------------------------------------------------------
wxSQLResultSet::wxSQLResultSet(void)
{
	mainstmt = 0;
	iseof =  true;
	isfirst = true;
	colcount = 0;
	isownstmt = false;
}
wxSQLResultSet::wxSQLResultSet(const wxSQLResultSet &set)
{
	mainstmt = set.mainstmt;
	const_cast<wxSQLResultSet&>(set).mainstmt = 0;
	iseof = set.iseof;
	isfirst = set.isfirst;
	colcount = set.colcount;
	isownstmt = set.isownstmt;
}
wxSQLResultSet::wxSQLResultSet(sqlite3 *db, sqlite3_stmt *stmt, bool eof, bool first, bool ownstmt)
{
	maindb = db;
	mainstmt = stmt;
	iseof = eof;
	isfirst = first;
	colcount = sqlite3_column_count(mainstmt);
	isownstmt = ownstmt;
}
wxSQLResultSet::~wxSQLResultSet(void)
{
	try {
		Finalize();
	}catch (...) {

	}

}
wxSQLResultSet &wxSQLResultSet::operator=(const wxSQLResultSet &result)
{
	try {
		Finalize();
	}catch (...) {

	}
	mainstmt = result.mainstmt;
	const_cast<wxSQLResultSet&>(result).mainstmt = 0;
	iseof = result.iseof;
	isfirst = result.isfirst;
	colcount = result.colcount;
	isownstmt = result.isownstmt;
	return *this;
}
bool wxSQLResultSet::CheckStmt(void)
{
	if (mainstmt == 0) {
		throw wxSQLException(WXSQL_ERROR, wxERRMSG_NOSTMT);
		return false;
	}
	return true;
}
int wxSQLResultSet::GetColumnCount(void)
{
	if (CheckStmt()) {
		return colcount;
	}
	return 0;
}
wxString wxSQLResultSet::GetColumnName(int cols)
{
	if (!CheckStmt()) {
		return wxEmptyString;
	}
	if (cols < 0 || cols > colcount-1){
		throw wxSQLException(WXSQL_ERROR, wxERRMSG_INVALID_INDEX);
	}
	const char *local = sqlite3_column_name(mainstmt, cols);
	return UTF8toWxString(local);
}
wxString wxSQLResultSet::GetDeclaredColumnType(int cols)
{
	if (!CheckStmt()) {
		return wxEmptyString;
	}
	if (cols < 0 || cols > colcount-1){
		throw wxSQLException(WXSQL_ERROR, wxERRMSG_INVALID_INDEX);
	}
	const char *local = sqlite3_column_decltype(mainstmt, cols);
	return UTF8toWxString(local);
}
int wxSQLResultSet::GetColumnType(int cols)
{
	if (!CheckStmt()) {
		return -1;
	}
	if (cols < 0 || cols > colcount-1){
		throw wxSQLException(WXSQL_ERROR, wxERRMSG_INVALID_INDEX);
	}
	return sqlite3_column_type(mainstmt, cols);
}
wxString wxSQLResultSet::GetDatabaseName(int cols)
{
	return wxEmptyString;
	/*if (!CheckStmt()) {
		return wxEmptyString;
	}
	if (cols < 0 || cols > colcount-1){
		throw wxSQLException(WXSQL_ERROR, wxERRMSG_INVALID_INDEX);
	}
	const char *local = sqlite3_column_database_name(mainstmt, cols);
	if (local != NULL) {
		return UTF8toWxString(local);
	}else{
		return wxEmptyString;
	}*/
}
wxString wxSQLResultSet::GetTableName(int cols)
{
	return wxEmptyString;
	/*if (!CheckStmt()) {
		return wxEmptyString;
	}
	if (cols < 0 || cols > colcount-1){
		throw wxSQLException(WXSQL_ERROR, wxERRMSG_INVALID_INDEX);
	}
	const char *local = sqlite3_column_table_name(mainstmt, cols);
	if (local != NULL) {
		return UTF8toWxString(local);
	}else{
		return wxEmptyString;
	}*/
}

int wxSQLResultSet::GetInt(int cols, int nullValue)
{
	if (GetColumnType(cols) == SQLITE_NULL) {
		return nullValue;
	}else{
		return sqlite3_column_int(mainstmt, cols);
	}
}
wxLongLong wxSQLResultSet::GetInt64(int cols, wxLongLong nullValue)
{
	if (GetColumnType(cols) == SQLITE_NULL) {
		return nullValue;
	}else{
		return wxLongLong(sqlite3_column_int64(mainstmt, cols));
	}

}
double wxSQLResultSet::GetDouble(int cols, double nullValue)
{
	if (GetColumnType(cols) == SQLITE_NULL) {
		return nullValue;
	}else{
		return sqlite3_column_double(mainstmt, cols);
	}

}
wxString wxSQLResultSet::GetString(int cols, const wxString &nullValue)
{
	if (GetColumnType(cols) == SQLITE_NULL) {
		return nullValue;
	}else{
		const char *local =  (const char *)sqlite3_column_text(mainstmt, cols);
		return UTF8toWxString(local);
	}
}
wxDateTime wxSQLResultSet::GetDate(int cols)
{
	if (GetColumnType(cols) == SQLITE_NULL) {
		return wxInvalidDateTime;
	}else{
		wxDateTime dt;
		if (dt.ParseDate(GetString(cols)) != NULL) {
			return dt;
		}else{
			return wxInvalidDateTime;
		}
	}
}
wxDateTime wxSQLResultSet::GetTime(int cols)
{
	if (GetColumnType(cols) == SQLITE_NULL) {
		return wxInvalidDateTime;
	}else{
		wxDateTime dt;
		if (dt.ParseTime(GetString(cols)) != NULL) {
			return dt;
		}else{
			return wxInvalidDateTime;
		}
	}
}
wxDateTime wxSQLResultSet::GetDateTime(int cols)
{
	if (GetColumnType(cols) == SQLITE_NULL) {
		return wxInvalidDateTime;
	}else{
		wxDateTime dt;
		if (dt.ParseDateTime(GetString(cols)) != NULL) {
			dt.SetMillisecond(0);
			return dt;
		}else{
			return wxInvalidDateTime;
		}
	}

}
bool wxSQLResultSet::GetBool(int cols)
{
	return GetInt(cols) != 0;
}
bool wxSQLResultSet::IsNull(int cols)
{
	return (GetColumnType(cols) == SQLITE_NULL);
}
bool wxSQLResultSet::Eof(void)
{
	CheckStmt();
	return iseof;
}
bool wxSQLResultSet::NextRow(void)
{
	CheckStmt();
	int rc;
	if (isfirst) {
		isfirst = false;
		rc = (iseof) ? SQLITE_DONE : SQLITE_ROW;
	}else{
		rc = sqlite3_step(mainstmt);
	}
	if (rc == SQLITE_DONE) {
		iseof = true;
		return false;
	}else if (rc == SQLITE_ROW) {
		return true;
	}else{
		rc = sqlite3_finalize(mainstmt);
		mainstmt = 0;
		const char *local = sqlite3_errmsg(maindb);
		throw wxSQLException(rc, UTF8toWxString(local));
	}
}
void wxSQLResultSet::Finalize(void)
{
	if (mainstmt && isownstmt) {
		int rc = sqlite3_finalize(mainstmt);
		mainstmt = 0;
		if (rc != SQLITE_OK) {
			const char *local = sqlite3_errmsg(maindb);
			throw wxSQLException(rc, UTF8toWxString(local));
		}
	}
}
wxString wxSQLResultSet::GetSQL(void)
{
	wxString sqlString = wxEmptyString;
#if SQLITE_VERSION_NUMBER >= 3005003
	CheckStmt();
	const char* sqlLocal = sqlite3_sql((sqlite3_stmt*) mainstmt);
	if (sqlLocal != NULL) sqlString = UTF8toWxString(sqlLocal);
#endif
	return sqlString;
}
int wxSQLResultSet::FindColumnIndex(const wxString &name)
{
	CheckStmt();

	wxCharBuffer colname = wxConvUTF8.cWC2MB(name.wc_str(*wxConvCurrent));
	const char *localcolname = colname;
	if (name.Len() > 0) {
		for (int colindex = 0; colindex < colcount; colindex++) {
			const char *temp = sqlite3_column_name(mainstmt, colindex);
			if (strcmp(localcolname,temp) == 0) {
				return colindex;
			}
		}
	}
	throw wxSQLException(WXSQL_ERROR, wxERRMSG_INVALID_INDEX);
}
//-----------------------------------------------------------
//  wxSQLDatabase class
//-----------------------------------------------------------
wxSQLDatabase::wxSQLDatabase(void)
{
	maindb = 0;
	timeoutMs = 60000;
}
wxSQLDatabase::~wxSQLDatabase(void)
{
	Close();
}
wxSQLDatabase& wxSQLDatabase::operator=(const wxSQLDatabase& db)
{
	maindb = db.maindb;
	timeoutMs = 60000;
	return *this;
}
void wxSQLDatabase::Open(const wxString &filename)
{
	wxCharBuffer strFileName = wxConvUTF8.cWC2MB(filename.wc_str(*wxConvCurrent));
	const char* localFileName = strFileName;

	int rc = sqlite3_open((const char*) localFileName, &maindb);

	if (rc != SQLITE_OK) {
		Close();
		const char* localError = sqlite3_errmsg(maindb);
		throw wxSQLException(rc, UTF8toWxString(localError));
	}
	SetBusyTimeout(timeoutMs);
}
bool wxSQLDatabase::IsOpen(void)
{
	return (maindb != NULL);
}
void wxSQLDatabase::Close(void)
{
	if (maindb) {
		sqlite3_close(maindb);
		maindb = 0;
	}
}
void wxSQLDatabase::Begin(SQLTransactionType type)
{
  wxString sql;
  switch (type)
  {
    case SQL_deferred_tran:
      sql << _T("begin deferred transaction");
      break;
    case SQL_immediate_tran:
      sql << _T("begin immediate transaction");
      break;
    case SQL_exclusive_tran:
      sql << _T("begin exclusive transaction");
      break;
    default:
      sql << _T("begin transaction");
      break;
  }
  ExecuteUpdate(sql);
}
void wxSQLDatabase::Commit(void)
{
	ExecuteUpdate("commit transaction");
}
void wxSQLDatabase::Rollback(void)
{
	ExecuteUpdate("rollback transaction");
}
bool wxSQLDatabase::GetAutoCommit(void)
{
	CheckDatabase();
	return sqlite3_get_autocommit(maindb) != 0;
}
bool wxSQLDatabase::TableExists(const wxString &table)
{
	wxSQLResultSet set = ExecuteQuery("select count(*) from sqlite_master where type='table' and name like ?");
	long value;
	set.NextRow();
	set.GetString(0).ToLong(&value);
	return (value > 0);
}
void wxSQLDatabase::CheckDatabase(void)
{
	if (!maindb) {
		throw wxSQLException(WXSQL_ERROR, wxERRMSG_NODB);
	}
}
bool wxSQLDatabase::CheckSyntax(const wxString &sql)
{
	wxCharBuffer strSql = wxConvUTF8.cWC2MB(sql.wc_str(*wxConvCurrent));
	const char *local = (const char *)strSql;
	return sqlite3_complete(local) != 0;
}
int wxSQLDatabase::ExecuteUpdate(const wxString &sql)
{
	wxCharBuffer strSql = wxConvUTF8.cWC2MB(sql.wc_str(*wxConvCurrent));
	const char* localSql = (const char *)strSql;
	return ExecuteUpdate(localSql);
}
int wxSQLDatabase::ExecuteUpdate(const char *sql)
{
	CheckDatabase();
	char *local = 0;
	int rc = sqlite3_exec(maindb, sql, 0, 0, &local);
	if (rc == SQLITE_OK) {
		return sqlite3_changes(maindb);
	}else{
		throw wxSQLException(rc, UTF8toWxString(local));	
	}
}
wxSQLResultSet wxSQLDatabase::ExecuteQuery(const wxString &sql)
{
	wxCharBuffer strSql = wxConvUTF8.cWC2MB(sql.wc_str(*wxConvCurrent));
	const char* localSql = (const char *)strSql;
	return ExecuteQuery(localSql);
}
wxSQLResultSet wxSQLDatabase::ExecuteQuery(const char *sql)
{
	CheckDatabase();
	const char *tail = 0;
	sqlite3_stmt *stmt;
	int rc = sqlite3_prepare_v2(maindb, sql, -1, &stmt, &tail);
	if (rc != SQLITE_OK) {
		const char* localError = sqlite3_errmsg(maindb);
		throw wxSQLException(rc, UTF8toWxString(localError));
	}
	rc = sqlite3_step(stmt);
	if (rc == SQLITE_DONE) {
		return wxSQLResultSet(maindb, stmt, true);
	}else if (rc == SQLITE_ROW) {
		return wxSQLResultSet(maindb, stmt, false);
	}else{
		rc = sqlite3_finalize(stmt);
		const char* localError= sqlite3_errmsg(maindb);
		throw wxSQLException(rc, UTF8toWxString(localError));
	}
}
int wxSQLDatabase::ExecuteScalar(const wxString &sql)
{
	wxCharBuffer strSql = wxConvUTF8.cWC2MB(sql.wc_str(*wxConvCurrent));
	const char* local = (const char *)strSql;
	return ExecuteScalar(local);
}
int wxSQLDatabase::ExecuteScalar(const char *sql)
{
	wxSQLResultSet result = ExecuteQuery(sql);

	if (result.Eof() || result.GetColumnCount() < 1) {
		throw wxSQLException(WXSQL_ERROR, wxERRMSG_INVALID_QUERY);
	}

	int value = 0;
	while (result.NextRow()) {
		value = result.GetInt(0);
		break;
	}
	return value;
}
void wxSQLDatabase::Interrupt(void)
{
	CheckDatabase();
	sqlite3_interrupt(maindb);
}
void wxSQLDatabase::SetBusyTimeout(int ms)
{
	CheckDatabase();
	timeoutMs = ms;
	sqlite3_busy_timeout(maindb, timeoutMs);
}
/*void wxSQLDatabase::SetCommitHook(wxSQLHook *hook)
{
	CheckDatabase();
	if (hook) {
		sqlite3_commit_hook(maindb, 
}
void wxSQLDatabase::SetRollbackHook(wxSQLHook *hook)
{

}
void wxSQLDatabase::SetUpdateHook(wxSQLHook *hook)
{

}*/
